package p47_autowired注入原理;

import org.springframework.beans.factory.BeanFactoryUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.beans.factory.config.BeanDefinition;
import org.springframework.beans.factory.config.BeanDefinitionHolder;
import org.springframework.beans.factory.config.DependencyDescriptor;
import org.springframework.beans.factory.support.DefaultListableBeanFactory;
import org.springframework.context.ConfigurableApplicationContext;
import org.springframework.context.annotation.AnnotationConfigApplicationContext;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.ContextAnnotationAutowireCandidateResolver;
import org.springframework.context.annotation.Primary;
import org.springframework.stereotype.Component;

import java.lang.reflect.Field;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;

/**
 * 上节中，注入时，找到目标bean的关键方法是DependencyDescriptor.doResolveDependency
 * doResolveDependency里面做了什么？这节进行介绍
 * 下面演示了使用@Autowired注入的7种情况特殊情况
 *
 * 本节知识点:
 * ContextAnnotationAutowireCandidateResolver的四大作用:
 *  1.
 */
@Configuration
public class _Autowired_doResolveDependency {
    public static void main(String[] args) {
        AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext(_Autowired_doResolveDependency.class);
        DefaultListableBeanFactory beanFactory = context.getDefaultListableBeanFactory();

        /**
         * 下面分别演示，当@Autowired修饰的属性或参数再不同情况下，doResolveDependency内部做的事
         */
        testArray(beanFactory);
        testList(beanFactory);
        testApplicationContext(beanFactory);
        testGeneric(beanFactory);
        testQualifier(beanFactory);
        testPrimary(beanFactory);
        testByName(beanFactory);
    }

    /**
     * doResolveDependency是如何解析Array的
     *   发现是Array类型，就得到数组元素类型
     *   从所有容器中找到该类型的bean的名字，然后根据名字找到所有bean，类型转换封装为数组
     */
    static Object testArray(DefaultListableBeanFactory beanFactory) {
        try {
            DependencyDescriptor dd = new DependencyDescriptor(Target.class.getDeclaredField("serviceArr"), false);
            if (dd.getDependencyType().isArray()) {
                //得到数组元素类型
                Class<?> componentType = dd.getDependencyType().getComponentType();
                //工具类，可以找到所有该类型bean的名字
                String[] names = BeanFactoryUtils.beanNamesForTypeIncludingAncestors(beanFactory, componentType);
                List<Object> beans = new ArrayList<>();
                for (String name : names) {
                    Object bean = beanFactory.getBean(name);
                    beans.add(bean);
                }
                //转换成数组
                Object array = beanFactory.getTypeConverter().convertIfNecessary(beans, dd.getDependencyType());
                return array;
            }

        } catch (Exception e) {
            e.printStackTrace();
        }
        return null;
    }

    /**
     * 如何解析List?
     * 根据泛型信息得到List的元素类型，其他步骤差不多
     */
    static Object testList(DefaultListableBeanFactory beanFactory) {
        try {
            DependencyDescriptor dd = new DependencyDescriptor(Target.class.getDeclaredField("serviceList"), false);
            if (dd.getDependencyType() == List.class) {
                //得到集合泛型类型
                Class<?> resolve = dd.getResolvableType().getGeneric().resolve();
                List<Object> list = new ArrayList<>();
                String[] names = BeanFactoryUtils.beanNamesForTypeIncludingAncestors(beanFactory, resolve);
                for (String name : names) {
                    //从容器中找到bean(本质就是beanFactory.getBean)
                    Object bean = dd.resolveCandidate(name, resolve, beanFactory);
                    list.add(bean);
                }
                //可以直接将list赋值给目标属性，因为泛型只有在编译时才检查,通过反射可以直接赋值
                return list;
            }
        } catch (Exception e) {
            e.printStackTrace();
        }
        return null;
    }

    /**
     * 解析特殊类型
     * 特殊类型，比如BeanFactory，ApplicationContext并没有放在单例bean的缓存map中(DefaultSingletonBeanRegistry的singletonObject属性中)，
     * 而是放在了DefaultListableBeanFactory对象的resolvableDependencies缓存中
     * context的refresh就会将这四个特殊类放入这个resolvableDependencies缓存中
     * 因此，就不能通过getBean的方法从容器直接获取bean
     */
    static Object testApplicationContext(DefaultListableBeanFactory beanFactory) {

        try {
            DependencyDescriptor dd = new DependencyDescriptor(Target.class.getDeclaredField("context"), false);
            //resolvableDependencies是私有属性，无法直接获取，而是通过反射获取
            Field resolvableDependencies = beanFactory.getClass().getDeclaredField("resolvableDependencies");
            resolvableDependencies.setAccessible(true);
            Map<Class<?>, Object> map = (Map<Class<?>, Object>)resolvableDependencies.get(beanFactory);

            //从这里的缓存，获取一个value注入给属性
            for (Map.Entry<Class<?>, Object> entry : map.entrySet()) {
                //判断entry.getKey()能否赋值给dd.getDependencyType()类型
                if (entry.getKey().isAssignableFrom(dd.getDependencyType())) {
                    //找到了，直接返回value
                    Object value = entry.getValue();
                    System.out.println(value);
                    return value;
                }
            }
        } catch (Exception e) {
            e.printStackTrace();
        }
        return null;
    }

    /**
     * 对于带有泛型的属性，怎么找到正确的bean注入呢？
     * 思路：
     *    1.得到属性的类型，Dao.class，从容器中找到所有该类型的bean
     *    2.根据这些bean找到他们对应的BeanDefinition
     *    3.根据BeanDefinition得到泛型信息，与要注入的属性进行比对，对比成功则注入
     */
    static Object testGeneric(DefaultListableBeanFactory beanFactory) {
        try {
            DependencyDescriptor dd = new DependencyDescriptor(Target.class.getDeclaredField("dao"), false);
            Class<?> dependencyType = dd.getDependencyType();
            //这个除了解析@Value和@Autowired，还能解析泛型，功能可谓强大
            ContextAnnotationAutowireCandidateResolver resolver = new ContextAnnotationAutowireCandidateResolver();
            resolver.setBeanFactory(beanFactory);
            for (String name : BeanFactoryUtils.beanNamesForTypeIncludingAncestors(beanFactory, dependencyType)) {
                BeanDefinition bd = beanFactory.getMergedBeanDefinition(name);
                //isAutowireCandidate可以比对BeanDefinition和要注入属性的泛型是否一致
                if (resolver.isAutowireCandidate(new BeanDefinitionHolder(bd, name), dd)) {
                    System.out.println("根据属性泛型找到bean=>"+name + "," + dd.resolveCandidate(name, dependencyType, beanFactory));
                    return dd.resolveCandidate(name, dependencyType, beanFactory);
                }
            }
        } catch (Exception e) {
            e.printStackTrace();
        }
        return null;
    }

    /**
     * 找多个符合条件的bean，选择哪一个注入？1
     * @Qualifier与@Autowired共同修饰的属性，是怎么解析的呢
     * @Qualifier进一步根据bean的名字进行匹配
     */
    static Object testQualifier(DefaultListableBeanFactory beanFactory) {
        try {
            DependencyDescriptor dd = new DependencyDescriptor(Target.class.getDeclaredField("service"), true);
            Class<?> dependencyType = dd.getDependencyType();
            //老朋友ContextAnnotationAutowireCandidateResolver又出现了，它还能解析@Qualifier找到
            ContextAnnotationAutowireCandidateResolver resolver = new ContextAnnotationAutowireCandidateResolver();
            resolver.setBeanFactory(beanFactory);
            for (String name : BeanFactoryUtils.beanNamesForTypeIncludingAncestors(beanFactory, dependencyType)) {
                //如果找到多个bean，则根据@Qualifier的值进一步筛选
                BeanDefinition beanDefinition = beanFactory.getMergedBeanDefinition(name);
                //比对beanDefinition的name，与要注入属性的@Qualifier的值是否匹配,isAutowireCandidate可以同时对比泛型和@Qualifier
                if (resolver.isAutowireCandidate(new BeanDefinitionHolder(beanDefinition, name), dd)) {
                    System.out.println("@Qualifier找到bean=>" + name + ", " + dd.resolveCandidate(name, dependencyType, beanFactory));
                    return dd.resolveCandidate(name, dependencyType, beanFactory);
                }
            }
        } catch (Exception e) {
            e.printStackTrace();
        }
        return null;
    }

    /**
     *找多个符合条件的bean，选择哪一个注入?
     * 没有@Qualifier指定的情况下，优先选择加了@Primary注解的bean
     * @Primary在组件扫描时就能被扫描到，作为BeanDefinition的成员变量，BeanDefinition的isPrimary方法就会得到是否加了这个注解
     * @Primary只能给同类型bean的一个使用
     */
    static Object testPrimary(DefaultListableBeanFactory beanFactory) {
        try {
            DependencyDescriptor dd = new DependencyDescriptor(Target.class.getDeclaredField("s"), true);
            Class<?> type = dd.getDependencyType();
            for (String name : BeanFactoryUtils.beanNamesForTypeIncludingAncestors(beanFactory, type)) {
                //找到多个bean，判断该bean是否加了@Primary
                if (beanFactory.getMergedBeanDefinition(name).isPrimary()) {
                    System.out.println("找到Primary Bean => " + dd.resolveCandidate(name, type, beanFactory));
                    return dd.resolveCandidate(name, type, beanFactory);
                }
            }
        } catch (Exception e) {

        }
        return null;
    }

    /**
     * 找多个符合条件的bean，选择哪一个注入？
     * 既没有@Qualifier指定的情况下，也没有@Primary，会根据属性名字进行匹配
     */
    static Object testByName(DefaultListableBeanFactory beanFactory) {
        try {
            DependencyDescriptor dd = new DependencyDescriptor(Target.class.getDeclaredField("service2"), true);
            Class<?> type = dd.getDependencyType();
            for (String name : BeanFactoryUtils.beanNamesForTypeIncludingAncestors(beanFactory, type)) {
                //根据名字匹配
                if (name.equals(dd.getDependencyName())) {
                    System.out.println("根据名字找到bean=>" + dd.resolveCandidate(name, type, beanFactory));
                    return dd.resolveCandidate(name, type, beanFactory);
                }
            }
        } catch (Exception e) {
            e.printStackTrace();
        }
        return null;
    }

    class Target {
        //数组
        @Autowired private Service[] serviceArr;
        //集合
        @Autowired private List<Service> serviceList;
        //特殊类
        @Autowired private ConfigurableApplicationContext context;
        //泛型
        @Autowired private Dao<Service1> dao;
        //发现多个同类型的bean，根据@Qualifier找到指定名称的bean
        @Autowired @Qualifier("service3") private Service service;
        //发现多个同类型的bean，也没有加@Qualifier时，使用@primay注解标注的bean
        @Autowired private Service none;
        //发现多个同类型的bean，既没有@Qualifier也没有@primay，根据属性名称找到对应名称的bean
        @Autowired private Service service2;    //这个找得到
    }

    interface Dao<T>{}
    @Component
    class Dao1 implements Dao<Service1> {}
    @Component
    class Dao2 implements Dao<Service2> {}

    interface Service{}
    @Component("service1") @Primary
    class Service1 implements Service{}
    @Component("service2")
    class Service2 implements Service{}
    @Component("service3")
    class Service3 implements Service{}
}
